﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Web.Configuration;

namespace Rarous.TexyNet {
  /// <summary>
  /// Texy slouží k převodu prostého textu se syntaxí Texy! na XHTML.
  /// </summary>
  /// <remarks>
  /// Texy je plně konfigurovatelná pomocí vlatní konfigurační sekce <c>rarous.texy</c>
  /// v konfiguračním souboru. Tato konfigurace se načítá vždy. Pokud sekce neexistuje
  /// pracuje Texy v Trust modu. Načtená nebo výchozí konfigurace se samozřejmě dá změnit
  /// programově v kódu. viz. ukázka.
  /// </remarks>
  /// <example>
  /// Ukázka použití Texy.net. Převádí vstupní text <c>inputText</c> v Texy! syntaxu na 
  /// výstupní text v Trust modu a Safe modu Texy!.
  /// <code>
  /// string inputText = @"== heading .[right]
  /// some text in **Texy!**" .[left]
  /// &lt;a href="http://codeplex.com/texynet/"&gt;&lt;strong&gt;Texy.net&lt;/strong&gt;&lt;/a&gt;
  /// ";
  /// Texy texy = new Texy(inputText);
  ///
  /// // Top heading on level three
  /// texy.HeadingModule.Top = 3;
  /// // allow 'left' and 'right' classes
  /// texy.AllowedClasses.Add("left");
  /// texy.AllowedClasses.Add("right");
  ///
  /// // Allow hyperlink tag with 'href' and 'title' attributes
  /// Tag hyperlink = new Tag("a");
  /// hyperlink.Attributes.Add("href");
  /// hyperlink.Attributes.Add("title");
  /// texy.AllowedTags.Add(hyperlink);
  ///
  /// // Allow 'strong' tag
  /// Tag strong = new Tag("strong");
  /// texy.AllowedTags.Add(strong);
  ///
  /// string outputTrustMode = texy.Process();
  /// texy.SafeMode = true;
  /// string outputSafeMode = texy.Process();
  /// </code> 
  /// Na rychlý převod je vhodné použít statickou metodu Process(), která se o všechno postará.
  /// <code>
  /// string inputText = @"== heading .[right]
  /// some text in **Texy!**" .[left]
  /// &lt;a href="http://codeplex.com/texynet/"&gt;&lt;strong&gt;Texy.net&lt;/strong&gt;&lt;/a&gt;
  /// ";
  /// string output = Texy.Process(inputText);
  /// </code>
  /// </example>
  public class Texy : ITexySettings {
    #region Fields
    private string _text;
    private bool _safeMode;
    private bool _singleLine;
    private bool _allowClasses;
    private bool _allowStyles;
    private bool _allowTags;
    private IList<String> _allowedClasses;
    private IList<String> _allowedStyles;
    private IList<Tag> _allowedTags;
    private bool _obfuscateEmail;
    private bool _mergeLines;
    private Modules.BlockModule _blockModule;
    private Modules.FormatterModule _formatterModule;
    private Modules.HeadingModule _headingModule;
    private Modules.ImageModule _imageModule;
    private Modules.ImageDescriptionModule _imageDescriptionModule;
    private Modules.LinkModule _linkModule;
    private Modules.SmiliesModule _smiliesModule;
    private Modules.QuoteModule _quoteModule;
    private Configuration.ConfigSectionHandler _configurationHandler;
    private Providers.ITexyProvider _provider;
    private bool _trustMode;
    #endregion

    #region Events
    /// <summary>
    /// Událost vyvolaná před zpracováním textu.
    /// </summary>
    public event EventHandler<EventArgs> PreProcess;
    /// <summary>
    /// Událost vyvolaná po zpracování textu.
    /// </summary>
    public event EventHandler<EventArgs> PostProcess;
    #endregion

    #region Constructors
    /// <summary>
    /// Konstruktor.
    /// </summary>
    /// <param name="text">Text k převodu na XHTML.</param>
    /// <param name="config">Konfigurační sekce.</param>
    protected Texy(string text, Configuration.ConfigSectionHandler config) {
      _text = text;
      _configurationHandler = config;
      _allowedClasses = new List<String>();
      _allowedStyles = new List<String>();
      _allowedTags = new List<Tag>();
      Initialize();
    }

    /// <summary>
    /// Konstruktor.
    /// </summary>
    /// <param name="text">Text k převodu na XHTML.</param>
    /// <param name="sectionName">Název konfigurační sekce.</param>
    protected Texy(string text, string sectionName)
      : this(text, ConfigurationManager.GetSection(sectionName) as Configuration.ConfigSectionHandler) {
    }

    /// <summary>
    /// Konstruktor. Přijímá text k převodu jako parametr.
    /// </summary>
    /// <param name="text">Text k převodu na XHTML.</param>
    public Texy(string text)
      : this(text, "rarous.texy") {
    }

    /// <summary>
    /// Konstruktor.
    /// </summary>
    public Texy()
      : this(String.Empty) {
    }
    #endregion

    #region Properties
    /// <summary>
    /// Příznak, zda-li má Texy! pracovat v Trust módu.
    /// </summary>
    public bool TrustMode {
      get {
        return _trustMode;
      }
      set {
        _trustMode = value;
        if (_trustMode) {
          SetTrustMode();
        }
      }
    }

    /// <summary>
    /// Příznak, zda-li má Texy! pracovat v Safe módu.
    /// </summary>
    public bool SafeMode {
      get {
        return _safeMode;
      }
      set {
        _safeMode = value;
      }
    }

    /// <summary>
    /// Příznak, zda-li má Texy! pracovat v jednořádkovém režimu.
    /// </summary>
    public bool SingleLine {
      get {
        return _singleLine;
      }
      set {
        _singleLine = value;
      }
    }

    /// <summary>
    /// Příznak, zda-li jsou povoleny třídy.
    /// </summary>
    public bool AllowClasses {
      get {
        return _allowClasses;
      }
      set {
        _allowClasses = value;
      }
    }

    /// <summary>
    /// Příznak, zda-li jsou povoleny styly.
    /// </summary>
    public bool AllowStyles {
      get {
        return _allowStyles;
      }
      set {
        _allowStyles = value;
      }
    }

    /// <summary>
    /// Příznak, zda-li jsou povoleny tagy.
    /// </summary>
    public bool AllowTags {
      get {
        return _allowTags;
      }
      set {
        _allowTags = value;
      }
    }

    /// <summary>
    /// Vrací seznam povolených tříd.
    /// </summary>
    public IList<String> AllowedClasses {
      get {
        return _allowedClasses;
      }
    }

    /// <summary>
    /// Vrací seznam povolených stylů.
    /// </summary>
    public IList<String> AllowedStyles {
      get {
        return _allowedStyles;
      }
    }

    /// <summary>
    /// Vrací nebo nastavuje seznam povolených tagů.
    /// </summary>
    public IList<Tag> AllowedTags {
      get {
        return _allowedTags;
      }
    }

    /// <summary>
    /// Příznak, zda-li má dojí k zakódování e-mailových adres (ochrana před spamboty).
    /// </summary>
    public bool ObfuscateEmail {
      get {
        return _obfuscateEmail;
      }
      set {
        _obfuscateEmail = value;
      }
    }

    /// <summary>
    /// Příznak, zda-li se mají zalamovat dlouhé řádky.
    /// </summary>
    public bool MergeLines {
      get {
        return _mergeLines;
      }
      set {
        _mergeLines = value;
      }
    }

    /// <summary>
    /// Nastavení modulu bloků.
    /// </summary>
    public Modules.BlockModule BlockModule {
      get {
        return _blockModule;
      }
    }

    /// <summary>
    /// Nastavení modulu formátovače výstupu.
    /// </summary>
    public Modules.FormatterModule FormatterModule {
      get {
        return _formatterModule;
      }
    }

    /// <summary>
    /// Nastavení modulu nadpisů.
    /// </summary>
    public Modules.HeadingModule HeadingModule {
      get {
        return _headingModule;
      }
    }

    /// <summary>
    /// Nastavení modulu obrázků.
    /// </summary>
    public Modules.ImageModule ImageModule {
      get {
        return _imageModule;
      }
    }

    /// <summary>
    /// Nastavení modulu obrázků s popiskem.
    /// </summary>
    public Modules.ImageDescriptionModule ImageDescriptionModule {
      get {
        return _imageDescriptionModule;
      }
    }

    /// <summary>
    /// Nastavení modulu odkazů.
    /// </summary>
    public Modules.LinkModule LinkModule {
      get {
        return _linkModule;
      }
    }

    /// <summary>
    /// Nastavení modulu emotikonů.
    /// </summary>
    public Modules.SmiliesModule SmiliesModule {
      get {
        return _smiliesModule;
      }
    }

    /// <summary>
    /// Nastavení modulu citací.
    /// </summary>
    public Modules.QuoteModule QuoteModule {
      get {
        return _quoteModule;
      }
    }

    /// <summary>
    /// Vraci objekt s konfiguraci Texy.net.
    /// </summary>
    protected Configuration.ConfigSectionHandler ConfigurationHandler {
      get {
        return _configurationHandler;
      }
    }

    /// <summary>
    /// Vraci objekt providera Texy.net.
    /// </summary>
    public Providers.ITexyProvider Provider {
      get {
        return _provider;
      }
      set {
        _provider = value;
      }
    }

    /// <summary>
    /// Vraci verzi provozovane Texy!.
    /// </summary>
    public string Version {
      get {
        return Provider.Version;
      }
    }

    /// <summary>
    /// Vraci nebo nastavuje vstupni text ke zformatovani.
    /// </summary>
    public string Text {
      get {
        return _text;
      }
      set {
        _text = value;
      }
    }
    #endregion

    #region Private Methods
    private void Initialize() {
      ReadConfiguration();
      InitializeProvider();
    }

    private void ReadConfiguration() {
      if (ConfigurationHandler == null) {
        SetTrustMode();
      }
      else {
        _obfuscateEmail = ConfigurationHandler.ObfuscateEmail;
        _mergeLines = ConfigurationHandler.MergeLines;
        _singleLine = ConfigurationHandler.SingleLine;
        _safeMode = ConfigurationHandler.SafeMode;

        ConfigureModules();
        ReadAllowedClasses();
        ReadAllowedStyles();
      }
    }

    private void InitializeProvider() {
      Providers.ITexyProvider xmlRpcProvider = new Providers.XmlRpcTexyProvider();
      if (ConfigurationHandler == null || ConfigurationHandler.Providers == null) {
        Provider = xmlRpcProvider;
        return;
      }

      Providers.TexyProviderCollection providers = new Providers.TexyProviderCollection();
      ProvidersHelper.InstantiateProviders(ConfigurationHandler.Providers, providers,
                                           typeof(Providers.TexyProvider));
      Providers.ITexyProvider configProvider = providers[ConfigurationHandler.DefaultProvider];
      if (configProvider == null) {
        Provider = xmlRpcProvider;
        return;
      }

      Provider = configProvider;
    }

    private void SetTrustMode() {
      _obfuscateEmail = true;
      _mergeLines = true;
      _blockModule = new Modules.BlockModule();
      _formatterModule = new Modules.FormatterModule();
      _headingModule = new Modules.HeadingModule();
      _imageModule = new Modules.ImageModule();
      _imageDescriptionModule = new Modules.ImageDescriptionModule();
      _linkModule = new Modules.LinkModule();
      _quoteModule = new Modules.QuoteModule();
      _smiliesModule = new Modules.SmiliesModule();
    }

    private void ConfigureModules() {
      _blockModule = new Modules.BlockModule(ConfigurationHandler.BlockModule);
      _formatterModule = new Modules.FormatterModule(ConfigurationHandler.FormatterModule);
      _headingModule = new Modules.HeadingModule(ConfigurationHandler.HeadingModule);
      _imageDescriptionModule =
        new Modules.ImageDescriptionModule(ConfigurationHandler.ImageDescriptionModule);
      _imageModule = new Modules.ImageModule(ConfigurationHandler.ImageModule);
      _linkModule = new Modules.LinkModule(ConfigurationHandler.LinkModule);
      _quoteModule = new Modules.QuoteModule(ConfigurationHandler.QuoteModule);
      _smiliesModule = new Modules.SmiliesModule(ConfigurationHandler.SmilesModule);
    }

    private void ReadAllowedClasses() {
      foreach (Configuration.StringElement cssClass in ConfigurationHandler.AllowedClasses) {
        _allowedClasses.Add(cssClass.Name);
      }
    }

    private void ReadAllowedStyles() {
      foreach (Configuration.StringElement style in ConfigurationHandler.AllowedStyles) {
        _allowedStyles.Add(style.Name);
      }
    }
    #endregion

    #region Protected Methods
    /// <summary>
    /// Vyvolá událost před zpracováním textu.
    /// </summary>
    protected void OnPreProcess() {
      if (PreProcess != null) {
        PreProcess(this, EventArgs.Empty);
      }
    }

    /// <summary>
    /// Vyvolá událost po zpracování textu.
    /// </summary>
    protected void OnPostProcess() {
      if (PostProcess != null) {
        PostProcess(this, EventArgs.Empty);
      }
    }
    #endregion

    #region Public Methods
    /// <summary>
    /// Převede vstupní text na XHTML.
    /// </summary>
    public virtual string Process() {
      if (_provider == null) {
        throw new NoProviderException("Provider is not specified.");
      }
      OnPreProcess();
      string result = _provider.Process(_text, this);
      OnPostProcess();
      return result;
    }
    #endregion

    #region Static Methods
    /// <summary>
    /// Převede vstupní text na XHTML.
    /// </summary>
    /// <param name="text">Vstupní text.</param>
    public static string Process(string text) {
      Texy texy = new Texy(text);
      return texy.Process();
    }
    #endregion
  }
}